﻿#include "Env.h"

#include "Exception.h"

namespace Lisp
{
  Env::Env(EnvPtr outer, const std::vector<std::string> &bindings, ListPtr args)
      : m_outer(outer)
  {
    size_t n = bindings.size();
    auto it = args->begin();
    const auto end = args->end();
    for (size_t i = 0; i < n; ++i)
    {
      if (bindings[i] == "&")
      {
        LISP_CHECK(i == n - 2, "There must be one parameter after &");
        Set(bindings[n - 1], ListVal(it, end));
        return;
      }
      LISP_CHECK(it != end, "Not enough arguments");
      Set(bindings[i], *it);
      ++it;
    }
    LISP_CHECK(it == end, "Too many arguments");
  }

  ValPtr Env::Get(const std::string &name)
  {
    for (Env *env = this; env; env = env->m_outer.get())
    {
      auto it = env->m_env.find(name);
      if (it != env->m_env.end())
      {
        return it->second;
      }
    }
    throw RuntimeException(name + " not found");
  }

  ValPtr Env::Set(const std::string &name, ValPtr value)
  {
    m_env[name] = value;
    return value;
  }

  EnvPtr Env::Find(const std::string &name)
  {
    for (Env *env = this; env; env = env->m_outer.get())
    {
      if (env->m_env.find(name) != env->m_env.end())
      {
        return env; // env->shared_from_this();
      }
    }
    return nullptr;
  }

  EnvPtr Env::GetRoot()
  {
    for (Env *env = this; env; env = env->m_outer.get())
    {
      if (!env->m_outer)
      {
        return env;// env->shared_from_this();
      }
    }
    return nullptr;
  }
}